The Microsoft OLE (or Object Linking and Embedding) 2.01 protocol provides a standard way for objects to fire events and provides a connection architecture for connecting event sources with event sinks. The event and connection architectures provided by Microsoft OLE 2.01 are in large part achieved by defining "interfaces." In Microsoft OLE 2.01, an "interface" is a named set of logically related functions. Each interface lists signatures for a set of functions, but does not provide code for implementing the set of functions; rather, objects provide the code for implementing the functions. Such objects are said to "support" the interfaces for which they provide code.
Microsoft OLE 2.01 specifies a component object model (COM). The component object model is implemented through a set of fundamental application program interface (API) functions and interfaces. The API functions provide for object creation, management and marshalling. A component object is one that complies with certain constraints specified in COM.
The event architecture enables an event source to source events that are sinked by an event sink. As will be described in more detail below, an event source triggers events on an event sink by calling methods on the event sink. Events are logically grouped into semantically-related event sets, and interfaces are defined for each event set that an event sink wishes to sink. An event sink may register, via interfaces, to sink events that are sourced by an event source. The registration occurs on a per-event set basis. The event sink and event source are typically separate processes and may reside on the same machine or on different machines.
The event source exposes connection points to which an event sink may connect. Each connection point is associated with a particular event set and indicates that the event source is ready to source events for the associated event set. A connection point may be connected by multiple connections to multiple event sinks (known as "multicasting). An event source may have multiple connection points (indicating the willingness of the event source to source multiple instances of event sets). The event source connection point container serves as a container of the connection points. Each connection point is typically implemented as a separate sub-object but need not be. Each connection point is assigned a globally unique identifier (GUID), which is a 128 bit value that uniquely identifies an interface or object.
FIG. 1 is a block diagram showing objects and interfaces supported by the objects that play a central role in the event architecture provided by Microsoft OLE 2.01. In FIG. 1, an event sink 10 is connected to both event source 12 and event source 14. Each of the event sources 12 and 14 is a connection point container that contains respective connection points 16A, 16B and 18A, 18B. Each of the event sources 12 and 14 supports the IConnectionPointContainer interface. The line with a circle attached to one end that is labeled "IConnectionPointContainer" serves as a graphical indicator to indicate that the object (i.e., event source 12 or 14) supports the member functions of the named interface. The IConnectionPointContainer interface permits an object to interrogate a connectable object regarding its outgoing interfaces. More specifically, the IConnectionPointContainer interface includes the following two functions (also referred to as "methods")
______________________________________ interface IConnectionPointContainer : public IUnknown HRESULT EnumConnectionPoints (IEnumConnectionPoints ** ppEnum); HRESULT FindConnectionpoint(REFIID iid, IConnectionPoint ** ppCP); }; ______________________________________
The EnumConnectionPoints() method enumerates the connection points contained within the connection point container. Microsoft OLE 2.01 defines a number of enumerator interfaces and methods that enumerate items so that list of items may be communicated and iterated through. This method is an example of such an enumerator method. The result of calling EnumConnectionPoints is that each of the connection points contained in the event source is enumerated. The FindConnectionPoint() method allows an object to interrogate another object to determine whether the other object supports an outgoing interface identified by the interface identifier (IID). If so, the other object returns, of the object, a connection point associated with a particular interface identifier (IID).
Each event set has an associated instance of an interface that the event sink supports. For example, in FIG. 1, event sink 10 supports a set of events in the ISomeEvents interface. The ISomeEvents interface includes a number of methods that may be called from a connection point to trigger associated events. Events are triggered by calling the associated method in the event set interfaces that the event sink 10 supports. Multiple connection points may call methods in a common event set interface. For example, in FIG. 1, connection point 16A and connection point 18A from event sources 12 and 14, respectively, may trigger calls to methods in the ISomeEvents interface instance that is provided by event sink 10. Likewise, it should be appreciated that an event sink may support multiple instances of event set interfaces. For example, in FIG. 1, the event sink 10 supports instances of both the ISomeEvents interface and the IOtherEvents interface. The event sink 10 supports multiple instances of the IOtherEvents interface. No source identity information is passed on an event invocation. Thus, an event sink must set up a forwarding interface for each source to which it is connected. For example, in FIG. 1 separate forwarding objects 19A and 19B have been registered for the separate instances of the IOtherEvents interface.
Each connection point 16A, 16B, 18A and 18B supports the IConnectionPoint interface. This interface includes the following five (5) functions (also referred to as "methods")
______________________________________ interface IConnectionPoint : public IUnknown HRESULT GetConnectionInterface(IID * pIID); HRESULT GetConnectionPointContainer (IConnectionPointContainer ** ppCPC); HRESULT Advise(IUnknown * pUnkSink, DWORD * pdwCookie); HRESULT Unadvise(DWORD dwCookie); HRESULT EnumConnections (TEnumConnections**PpEnum); }; ______________________________________
The GetConnectionInterface() method names the event set interface which the connection point is willing to call on the event sink to trigger events. Thus, for connection point 16A, calling the GetConnectionInterface() function returns the IID for the instance of the ISomeEvents interface provided by the event sink 10. The GetConnectionPointContainer() method allows an event sink or other caller to navigate back to the connection point container that contains the connection point to the event sink. The Advise() method creates an advisory connection with an event sink, and the Unadvise() method breaks the connection. The EnumConnections() method enumerates the event sinks that are currently connected to the connection point.
FIG. 2 is a flowchart illustrating the steps that are performed by an event sink to establish a connection with an event source and begin the process of firing events. Initially, the event sink obtains an interface pointer for the IConnectionPointContainer interface from the event source to which it seeks to connect. Recall that Microsoft OLE 2.01 provides the QueryInterface() method that returns the interface pointer for a requested interface. Thus, the event sink obtains the interface pointer for the IConnectionPointContainer by calling the QueryInterface() method on the event source (step 24 in FIG. 2). For the example shown in FIG. 1, the event sink 10 calls QueryInterface() on both event source 12 and event source 14 to obtain an interface pointer for the IConnectionPointContainer interface. The event sink 10 then calls the FindConnectionPoint() method for each event set that it wishes the event source(s) to source (step 26 in FIG. 2). For example, in FIG. 1, the event sink 10 calls the FindConnectionPoint() method twice to locate connection points 16A and 16B for event source 12. Similarly, the event sink 10 calls the FindConnectionPoint() method twice to locate connection points 18A and 18B on event source 14.
The connection of the event sink to each of the connection points is completed by calling the Advise() method in the IConnectionPoint interface for each of the connection points (step 28 in FIG. 2). In the example shown in FIG. 1, the Advise() method in the respective instances of the IConnectionPoint interface supported by connection points 16A, 16B, 18A and 18B is called. Once these connections are realized, the event sources 12 and 14 may fire events through the event set interfaces (step 30 in FIG. 1).
Events are called on a single thread of control in a synchronous fashion to provide a single unbroken path of execution. Events may be called locally or across process and machine boundaries. Whether the called object is local or remote is largely transparent to the caller due to the mechanisms provided by OLE. Calling threads are blocked until the call returns. The blocking mechanism may vary depending on whether the call is local or remote.
One implication of this model is that the responsiveness of invoking the component depends on the ability of the invoked component to return control to the invoking component. This approach is adopted with each of the possible threading models for objects that COM supports. COM supports a single threaded model where all calls into the event sink are synchronized with the message queue for the thread on which the event sink object was created. This approach ensures that the event sink is guaranteed to only be called to process events on a single thread of execution. COM also supports an apartment model where objects created on a thread are guaranteed only to be called on that thread. Thus, the event sinks are guaranteed to be called only on the thread that created the event sink. A final threading model that can support a free threading model where in theory, multiple threads may concurrently call on the event sink. The event sink uses a synchronization mechanism to limit concurrent access to the event sink. The underlying threading models, however, appear to be the same to the caller because the remote procedure call (or "RPC") model has been made transparent.
Event sinks and event sources often reside on separate machines. Microsoft OLE 2.01 provides a mechanism for marshalling method calls so that the calls may be transmitted between a client and a server. A "client" calls methods and a "server" implements methods which are called. In the events context, event sinks and event sources assume the roles of both client and server at various times. When the event sink calls on the IConnectionPointContainer and IConnectionPoint interfaces, the event sink acts as a client and the event source acts as a server. Once the connection is realized and the event source begins making calls on the event sink, the event source acts as the client and the event sink acts as the server.
The marshalling mechanism used in the event architecture allows the client to act as if the server being called exists locally in the process space of the client even if the server is a separate process running on a distinct machine. The marshalling mechanism enhances conventional RPC to allow the remoting of whole interfaces rather than single procedure calls.
FIG. 3 is a block diagram illustrating several of the major components that play a role in the marshalling mechanism provided. On the client side, a proxy 36 implements the methods of the interface being remoted. When the client 32 invokes a method on the proxy 36, the proxy 36 copies all of the in parameters of the method being invoked into a single linear buffer and outputs a message containing a pointer to the buffer and meta-data about the method being invoked. The proxy 36 supports the IRpcProxyBuffer interface. The proxy manager 38 is responsible for loading and unloading proxies for communication with the server 34. The proxy manager 38 is also responsible for managing the establishment and cleanup of the channel buffer 40. The channel buffer 40 encapsulates knowledge about the underlying transport mechanism used by the transport 41. The central role of the channel buffer 40 is to maintain the connection between the proxy 36 and the stub 42.
The server side includes the stub 42, which takes the buffer that has been filled by the proxy 36 and initiates a call on the intended method on the server 34. The stub 42 also receives out parameters from the server 34 when the server has completed the method invocation. The stub 42 marshalls the out parameters into a linear buffer and passes this linear buffer to the channel buffer 46. The stub 42 supports the IRpcStubBuffer interface. The stub manager 44 handles the freeing of stubs when they are no longer needed. In addition, the stub manager 44 cooperates with the proxy manager 38. The stub manager 44 is responsible for maintaining a list of all stubs loaded for a given remote client. The channel buffer 46 communicates with channel buffer 40 to translate the buffer holding the out parameters back to the proxy 36 which unmarshalls the out parameters, etc.
FIG. 4 is a flowchart illustrating the steps that are performed in a method invocation from a client to a server when the client and server exist in different execution contexts. Initially, the client 32 calls a method on the proxy 36 (step 50 in FIG. 4). The proxy 36 computes the size of the marshalled data and calls the GetBuffer() method of the IRpcChannelBuffer interface supported by channel buffer 40 (step 52 in FIG. 4). The GetBuffer() method obtains memory for the linear buffer for transmitting the marshalled data to the server 34. The in parameters are then copied into the buffer, and the SendReceive() method of the IRpcChannelBuffer interface supported by channel buffer 40 is called to transmit the method call to the server 34 (step 54 in FIG. 4). The proxy thread of execution is blocked awaiting the results of the remote call.
Channel buffer 40 then passes the buffer to the channel buffer 46 on the server side via the transport mechanism 41 (step 56 in FIG. 4). The channel buffer 46 on the server side receives the transmitted buffer from the transport mechanism 41 on a thread of execution allocated in the server context by the RPC system and calls the Invoke() method on the stub 42 (step 58 in FIG. 4). The thread of execution from the channel buffer 46 is blocked in the stub 42 until the method call returns. The Invokeo() method is used to invoke a particular method through the stub 42. The stub 42 determines the method being called and sets up the parameters to be passed to the server 34. The stub 42 invokes the desired method on the server 34 using the identical in parameters that were passed from the client 32 (step 60 in FIG. 4). The method is executed on the server 34, and the server returns out parameters to the stub 42 (step 62 in FIG. 4). The stub 42 examines the out parameters and calls the GetBuffer() method of the IRpcStubBuffer interface that it supports to allocate a buffer in which to return the out parameters to the client 32 (step 64 in FIG. 4). The out parameters are then copied into the allocated buffer, and the stub 42 calls FreeBuffer() method to free the buffer for the in parameters (step 66 in FIG. 4).
Channel buffer 46 on the server side calls transport mechanism 41 to send a buffer holding the out parameters back to the client 32 (step 68 in FIG. 4). The channel buffer 40 at the client 32 receives the linear buffer holding the out parameters and passes the linear buffer to the proxy 36 (step 70 in FIG. 4). The buffer is returned as the return from the SendReceive() method call that was earlier initiated. The proxy 36 unmarshalls the out parameters that have been transmitted in the buffer and sets up the internal stack for the remote call return. The proxy 36 then returns to the client 32 (step 72 in FIG. 4).
Unfortunately, the event and connection architecture provided by Microsoft OLE 2.01 is also limited in several additional respects which may affect the responsiveness or efficiency of programs written in accordance with the model. The architecture is limited in that it does not facilitate bidirectionality of connections such that an event sink may pass back control information that primes, focuses and filters the events which the event sink considers to be of interest. An application interested in sparse events in a rich stream is forced to process all events in the stream. When interest in a set of events changes, an existing event connection must be disconnected and another event connection must be created in order to receive changes. An additional limitation is that this architecture does not support asynchronous event delivery. Events are delivered synchronously. This architecture also suffers from either a lack of responsiveness or the creation of an excess number of threads to ensure responsiveness. In general, the responsiveness of an event source is limited by the ability of the event sink to process events sent from the event source to the event sink in a timely fashion. Thus, if an insufficient number of threads are allocated, responsiveness suffers. Conversely, to guarantee responsiveness, the system must create an excessive number of threads. Another limitation of this architecture is that it does not efficiently facilitate disparate threading models between an event source and an event sink.
The architecture provided by Microsoft.RTM. OLE 2.01 is also limited in that it does not allow third party objects to create connections. Objects must know a priori to whom they are connected. The connection mechanism is non-polymorphic.